<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>烟花</title>
    <style type="text/css">
    body{
        margin: 0;
        padding: 0;
    }
    </style>
</head>
<body>

    <canvas id="canvas">Canvas not supported</canvas>

    <script type="text/javascript">
        
        window.requestAnimationFrame = (function(){
            return window.requestAnimationFrame ||
                    window.webkitRequestAnimationFrame || 
                    window.mozRequestAnimationFrame ||
                    function(callback){
                        window.setTimeout(1000/60);
                    }
        })();

        var canvas = document.getElementById("canvas"),
            context = canvas.getContext("2d"),
            cw = window.innerWidth,
            ch = window.innerHeight,
            fireworks = [],
            particles = [],
            hue = 120,
            limiterTotal = 5,
            limiterTick =0,
            timerTotal = 80,
            timerTick = 0,
            mousedown = false,
            mx,
            my;

        canvas.width = cw;
        canvas.height = ch;

        function random(min, max){
            return Math.random()*(max-min)+min;
        }

        function calculateDistance(p1x, p1y, p2x, p2y){
            var xDistance = p1x - p2x,
                yDistance = p1y - p2y;
            return Math.sqrt(Math.pow(xDistance, 2) + Math.pow(yDistance, 2));
        }

        function Firework(sx, sy, tx, ty){
            this.x = sx;
            this.y = sy;

            this.sx = sx;
            this.sy = sy;

            this.tx = tx;
            this.ty = ty;

            this.distanceToTarget = calculateDistance(sx, sy, tx, ty);
            this.distanceTraveled = 0;
            this.coordinates = [];

            this.coordinateCount = 3;
            while(this.coordinateCount--){
                this.coordinates.push([this.x, this.y]);
            }

            this.angle = Math.atan2(ty-sy, tx-sx);
            this.speed = 2;
            this.acceleration = 1.02;
            this.brightness = random(50, 70);
            this.targetRadius = 1;
        }

        Firework.prototype.update = function(index){
            // 更新半径
            this.coordinates.pop();
            this.coordinates.unshift([this.x, this.y]);
            if(this.targetRadius < 8){
                this.targetRadius += 0.3;
            }else{
                this.targetRadius = 1;
            }

            this.speed*=this.acceleration;
            var vx = Math.cos(this.angle)*this.speed;
            var vy = Math.sin(this.angle)*this.speed;

            this.distanceTraveled = calculateDistance(this.sx, this.sy, this.x+vx, this.y+vy);

            if(this.distanceTraveled >= this.distanceToTarget){
                createParticles(this.tx, this.ty);
                fireworks.splice(index,1);
            }else{
                this.x += vx;
                this.y += vy;
            }
        }

        Firework.prototype.draw = function(){
            context.beginPath();
            context.moveTo(this.coordinates[this.coordinates.length-1][0], this.coordinates[this.coordinates.length-1][1]);
            context.lineTo(this.x, this.y);
            context.strokeStyle = "hsl("+hue+",100%,"+this.brightness+"%)";
            context.stroke();

            context.beginPath();
            context.arc(this.tx, this.ty, this.targetRadius, 0, Math.PI*2);
            context.stroke();
        }

        function Particle(x, y){
            this.x = x; 
            this.y = y;
            this.coordinates = [];
            this.coordinateCount = 5;
            while(this.coordinateCount--){
                this.coordinates.push([this.x, this.y]);
            }

            this.angle = random(0, Math.PI*2);
            this.speed = random(1, 15); // 速度
            this.friction = 0.95; // 摩擦力
            this.gravity = 1; // 重力
            this.hue = random(hue-20, hue+20); 
            this.brightness = random(50, 80); // 亮度
            this.alpha = 1; // 透明度
            this.decay = random(0.015, 0.03); //衰弱
        }

        Particle.prototype.update = function(index){
            // 更新移动轨迹
            this.coordinates.pop();
            this.coordinates.unshift([this.x, this.y]);
            this.speed *= this.friction;
            this.x += Math.cos(this.angle)*this.speed;
            this.y += Math.sin(this.angle)*this.speed + this.gravity;
            this.alpha -= this.decay;

            if(this.alpha <= this.decay){
                particles.splice(index, 1);
            }
        }

        Particle.prototype.draw = function(){
            context.beginPath();
            context.moveTo(this.coordinates[this.coordinates.length-1][0], this.coordinates[this.coordinates.length-1][1]);
            context.lineTo(this.x, this.y);
            context.strokeStyle="hsla("+this.hue+",100%"+this.brightness+"%,"+this.alpha+")";
            context.stroke();
        }

        function createParticles(x,y){
            var particleCount = 300;
            while (particleCount--) {
                particles.push(new Particle(x, y));
            }
        }

        function loop(){
            requestAnimationFrame(loop);
            hue += 0.5;
            context.globalCompositeOperation = "destination-out";
            context.fillStyle = "rgba(0,0,0,0.2)";
            context.fillRect(0,0,cw,ch);
            context.globalCompositeOperation = "lighter";
            context.fillRect(0,0,cw,ch);
            var i = fireworks.length;
            while (i--) {
                fireworks[i].draw();
                fireworks[i].update(i);
            }

            var i = particles.length;
            while (i--) {
                particles[i].draw();
                particles[i].update(i);
            }

            if(timerTick >= timerTotal){
                if(!mousedown){
                    fireworks.push(new Firework(cw/2, ch, random(0, cw), random(0, ch/2)));
                    timerTick = 0;
                }
            }else{
                timerTick++;
            }

            if(limiterTick >= limiterTotal){
                if(mousedown){
                    fireworks.push(new Firework(cw/2, ch, mx, my));
                    limiterTick = 0;
                }
            }else{
                limiterTick++;
            }
        }

        function touchmove(e){
            if(e.type.indexOf("touch")>=0){
                mx = e.touches[0].clientX - canvas.offsetLeft;
                my = e.touches[0].clientY - canvas.offsetTop;
            }else{
                mx = e.pageX - canvas.offsetLeft;
                my = e.pageY - canvas.offsetTop;
            }
        }

        function touchstart(e){
            e.preventDefault();
            mousedown = true;
        }

        canvas.addEventListener('mousemove', function(e){
            touchmove(e);
        })
        canvas.addEventListener('touchmove', function(e){
            touchmove(e);
        })

        canvas.addEventListener('mousedown', function(e){
            touchstart(e);
        })
        canvas.addEventListener('touchstart', function(e){
            touchstart(e);
        })

        canvas.addEventListener("mouseup", function(e){
            e.preventDefault();
            mousedown = false;
        })

        canvas.addEventListener("touchend", function(e){
            e.preventDefault();
            mousedown = false;
        })

        window.onload = loop;




    </script>
</body>
</html>